#include "GameApplication.h"
#include "Grid.h"
#include "PipeCube.h"
#include "ObstacleCube.h"

//-------------------------------------------------------------------------------------
GameApplication::GameApplication(void): // Lecture 8
mCurrentObject(0),
mCurrentCube(0),
bLMouseDown(false),
bRMouseDown(false),
grid( new Grid(4, 5) )
{
}

//-------------------------------------------------------------------------------------
GameApplication::~GameApplication(void)
{
}

//-------------------------------------------------------------------------------------
void GameApplication::createScene(void)
{
	loadEnv();
	setupEnv();
	loadObjects();
	loadCharacters();
	srand( time(NULL) );
}

void // Load the buildings or ground plane, etc
GameApplication::loadEnv()
{
	mSceneMgr->setSkyBox(true, "Examples/StormySkyBox");
}


void // Set up lights, shadows, etc
GameApplication::setupEnv()
{
	using namespace Ogre;

	// set shadow properties
	mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_ADDITIVE);
	mSceneMgr->setShadowColour(ColourValue(0.5f, 0.5f, 0.5f));
	mSceneMgr->setShadowTextureSize(1024);
	mSceneMgr->setShadowTextureCount(1);

	// disable default camera control so the character can do its own
	mCameraMan->setStyle(OgreBites::CS_MANUAL); //CS_FREELOOK, CS_ORBIT, CS_MANUAL

	// use a small amount of ambient lighting
	mSceneMgr->setAmbientLight(ColourValue::White);

	// add a bright light above the scene
	Light* light = mSceneMgr->createLight();
	light->setType(Light::LT_POINT);
	light->setPosition(-10, 40, 20);
	light->setSpecularColour(ColourValue::White);
	light->setVisible(true);

	//////////////////////////////////////////////////////////////////////////////////
	// Lecture 8
	//but we also want to set up our raySceneQuery after everything has been initialized
	mRayScnQuery = mSceneMgr->createRayQuery(Ogre::Ray());
}

void // Load other propers or objects (e.g. furniture)
GameApplication::loadObjects()
{
	currentLevel = new Level();
	mCurrentCube = currentLevel->getStartCube();
	cubeList = currentLevel->getCubeList();
}

void // Load actors, agents or characters
GameApplication::loadCharacters()
{
}

void GameApplication::addTime(Ogre::Real deltaTime)
{
	//std::list<Cube*>::iterator it;
	//for(it = cubeList->begin(); it != cubeList->end(); it++)
	//	(*it)->update(deltaTime);

	currentLevel->update(deltaTime);
	if(currentLevel->isFinished())
	{
		delete(currentLevel);
		currentLevel = new Level();
	}
}


//-------------------------------------------------------------------------------------
// Lecture 7 (moved into GameApplication)
bool GameApplication::keyPressed( const OIS::KeyEvent &arg )
{
    if (mTrayMgr->isDialogVisible()) return true;   // don't process any more keys if dialog is up

	if (arg.key == OIS::KC_ESCAPE)
    {
        mShutDown = true;
    }
	else if (arg.key == OIS::KC_A)
	{
		if( mCurrentCube && ((std::string)(typeid(*mCurrentCube).name())).compare("class PipeCube") == 0 ) //COOL!!! typeid() is almost like instanceof in Java :-D
			((PipeCube*)mCurrentCube)->rotateLeft();
	}
	else if (arg.key == OIS::KC_D)
	{
		if( mCurrentCube && ((std::string)(typeid(*mCurrentCube).name())).compare("class PipeCube") == 0 )
			((PipeCube*)mCurrentCube)->rotateRight();
	}
	else if (arg.key == OIS::KC_W)
	{
		if( mCurrentCube && ((std::string)(typeid(*mCurrentCube).name())).compare("class PipeCube") == 0 )
			((PipeCube*)mCurrentCube)->rotateForward();
	}
	else if (arg.key == OIS::KC_S)
	{
		if( mCurrentCube && ((std::string)(typeid(*mCurrentCube).name())).compare("class PipeCube") == 0 )
			((PipeCube*)mCurrentCube)->rotateBackward();
	}
	else if (arg.key == OIS::KC_E)
	{
		if( mCurrentCube && ((std::string)(typeid(*mCurrentCube).name())).compare("class PipeCube") == 0 )
			((PipeCube*)mCurrentCube)->rotateClock();
	}
	else if (arg.key == OIS::KC_Q)
	{
		if( mCurrentCube && ((std::string)(typeid(*mCurrentCube).name())).compare("class PipeCube") == 0 )
			((PipeCube*)mCurrentCube)->rotateCountClock();
	}
	else if (arg.key == OIS::KC_RETURN)
		mCurrentCube = currentLevel->checkNextConnectivity();
	else if (arg.key == OIS::KC_SPACE)
	{
		if( mCurrentCube && ((std::string)(typeid(*mCurrentCube).name())).compare("class ObstacleCube") == 0 )
		{
			cubeList->remove(mCurrentCube);
//			mCurrentCube = ((ObstacleCube*)mCurrentCube)->changeToPipeCube();
			cubeList->push_back( mCurrentCube );
		}
	}
    else if (arg.key == OIS::KC_F)   // toggle visibility of advanced frame stats
    {
        mTrayMgr->toggleAdvancedFrameStats();
    }
    else if (arg.key == OIS::KC_G)   // toggle visibility of even rarer debugging details
    {
        if (mDetailsPanel->getTrayLocation() == OgreBites::TL_NONE)
        {
            mTrayMgr->moveWidgetToTray(mDetailsPanel, OgreBites::TL_TOPRIGHT, 0);
            mDetailsPanel->show();
        }
        else
        {
            mTrayMgr->removeWidgetFromTray(mDetailsPanel);
            mDetailsPanel->hide();
        }
    }
    else if (arg.key == OIS::KC_T)   // cycle polygon rendering mode
    {
        Ogre::String newVal;
        Ogre::TextureFilterOptions tfo;
        unsigned int aniso;

        switch (mDetailsPanel->getParamValue(9).asUTF8()[0])
        {
        case 'B':
            newVal = "Trilinear";
            tfo = Ogre::TFO_TRILINEAR;
            aniso = 1;
            break;
        case 'T':
            newVal = "Anisotropic";
            tfo = Ogre::TFO_ANISOTROPIC;
            aniso = 8;
            break;
        case 'A':
            newVal = "None";
            tfo = Ogre::TFO_NONE;
            aniso = 1;
            break;
        default:
            newVal = "Bilinear";
            tfo = Ogre::TFO_BILINEAR;
            aniso = 1;
        }

        Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
        Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(aniso);
        mDetailsPanel->setParamValue(9, newVal);
    }
    else if (arg.key == OIS::KC_R)   // cycle polygon rendering mode
    {
        Ogre::String newVal;
        Ogre::PolygonMode pm;

        switch (mCamera->getPolygonMode())
        {
        case Ogre::PM_SOLID:
            newVal = "Wireframe";
            pm = Ogre::PM_WIREFRAME;
            break;
        case Ogre::PM_WIREFRAME:
            newVal = "Points";
            pm = Ogre::PM_POINTS;
            break;
        default:
            newVal = "Solid";
            pm = Ogre::PM_SOLID;
        }

        mCamera->setPolygonMode(pm);
        mDetailsPanel->setParamValue(10, newVal);
    }
    else if(arg.key == OIS::KC_F5)   // refresh all textures
    {
        Ogre::TextureManager::getSingleton().reloadAll();
    }
    else if (arg.key == OIS::KC_SYSRQ)   // take a screenshot
    {
        mWindow->writeContentsToTimestampedFile("screenshot", ".jpg");
    }

    mCameraMan->injectKeyDown(arg);
    return true;
}

bool GameApplication::keyReleased( const OIS::KeyEvent &arg )
{

    mCameraMan->injectKeyUp(arg);
    return true;
}

bool GameApplication::mouseMoved( const OIS::MouseEvent &arg )
{
	// Lecture 8
	//if the left mouse button is held down
	if(bLMouseDown)
	{
		//find the current mouse position
		//CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();
		Ogre::Vector3 mousePos; 
		mousePos.x = arg.state.X.abs;
		mousePos.y = arg.state.Y.abs;
		mousePos.z = arg.state.Z.abs;
 
		//create a raycast straight out from the camera at the mouse's location
		Ogre::Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.x/float(arg.state.width), mousePos.y/float(arg.state.height));
		mRayScnQuery->setRay(mouseRay);
		mRayScnQuery->setSortByDistance(false);	//world geometry is at the end of the list if we sort it, so lets not do that
 
		Ogre::RaySceneQueryResult& result = mRayScnQuery->execute();
	}
	else if(bRMouseDown)	//if the right mouse button is held down...
	{
		
	}
	/////////////////////////////////////////////////////////////////////////////////////////
    if (mTrayMgr->injectMouseMove(arg)) return true;
    mCameraMan->injectMouseMove(arg);
    return true;
}

bool GameApplication::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	//////////////////////////////////////////////////////////////////////////////////////
	// Lecture 8
	if(id == OIS::MB_Left)
	{
		Ogre::SceneNode* mLastObject = mCurrentObject; //Used to remove the bounding box from the last object

		//find the current mouse position
		//CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();
		Ogre::Vector3 mousePos;
		mousePos.x = arg.state.X.abs;
		mousePos.y = arg.state.Y.abs;
		mousePos.z = arg.state.Z.abs;
		
 
		//then send a raycast straight out from the camera at the mouse's position
		Ogre::Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.x/float(arg.state.width), mousePos.y/float(arg.state.height));
		mRayScnQuery->setRay(mouseRay);
		mRayScnQuery->setSortByDistance(true);
 

		Ogre::RaySceneQueryResult& result = mRayScnQuery->execute();
		Ogre::RaySceneQueryResult::iterator iter = result.begin();
 
		for(iter; iter != result.end(); iter++)
		{
			//if you clicked on a robot or ninja it becomes selected
			if(iter->movable && iter->movable->getName().substr(0, 5) != "tile[")
			{
				mCurrentObject = iter->movable->getParentSceneNode();
				break;
			}
		}

		//show that the current object has been deselected by removing the bounding box visual
		//if(mLastObject)
		//{
		//	mLastObject->showBoundingBox(false);
		//}
 
		//now we show the bounding box so the user can see that this object is selected and find which cube it pertains to
		if(mCurrentObject)
		{
			//mCurrentObject->showBoundingBox(true);

			std::string objName = mCurrentObject->getName();
			std::list<Cube*>::iterator it;
			for( it = cubeList->begin(); it != cubeList->end(); it++ ) //We need to find which cube we're dealing with
			{
				if( objName.compare((*it)->getName()) == 0 ) //We've found the cube we were looking for
				{
					mCurrentCube = (*it);
					currentLevel->updateCurrentCube(mCurrentCube);
					break;
				}
			}
		}
 
		bLMouseDown = true;
	}
	else if (id == OIS::MB_Right) //We can do something (maybe swapping?) with this
		bRMouseDown = true;

	//////////////////////////////////////////////////////////////////////////////////////
    if (mTrayMgr->injectMouseDown(arg, id)) return true;
    mCameraMan->injectMouseDown(arg, id);
    return true;
}

bool GameApplication::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	// Lecture 8
	if(id  == OIS::MB_Left)
	{
		bLMouseDown = false;
	}
	if(id  == OIS::MB_Right)
	{
		bRMouseDown = false;
	}
	else
		mCameraMan->injectMouseUp(arg, id);

	//////////////////////////////////////////////////////////////////////////////
    if (mTrayMgr->injectMouseUp(arg, id)) return true;
    mCameraMan->injectMouseUp(arg, id);
    return true;
}
